/**********************************************
*  Classes:   CdnResourceLoader
*  Desc:      Manager that loads all resources
*			  that are supported by Coden. Singleton
*			  class
*  Author:    Vaios Kalpias-Ilias (C) 2008-2009
***********************************************/

#include "CdnResourceLoader.h"
#include "GeometryNode.h"
#include "TextureProperty.h"
#include "CdnRenderer.h"
#include "Loader3ds.h"
#include "LoaderModel.h"
#include "LoaderBmpGL.h"
#include "LoaderTgaGL.h"

//------------------------------------------------------
CdnResourceLoader* CdnResourceLoader::m_pInstance = 0;
//------------------------------------------------------
/**
* Ctor
*/
CdnResourceLoader::CdnResourceLoader()
: m_pRenderer(CdnRenderer::GetInstance())
{
}
//------------------------------------------------------
/**
* Dtor
*/
CdnResourceLoader::~CdnResourceLoader()
{	
	UnloadResources();
}
//------------------------------------------------------
/**
* Returns single instance
*/
CdnResourceLoader* CdnResourceLoader::GetInstance()
{
	if (!m_pInstance)
	{
		m_pInstance = new CdnResourceLoader();
	}
	
	return m_pInstance;
}
//------------------------------------------------------
/**
* Destroys single instance
*/
void CdnResourceLoader::Destroy()
{
	if (m_pInstance)
	{
		delete m_pInstance;
		m_pInstance = 0;
	}
}
//------------------------------------------------------
/**
* Loads a geometry file
*/
GeometryNodePtr CdnResourceLoader::LoadGeometryResource(const char* filename)
{
	// check filename extension
	if (filename)
	{
		// see if the geometry is already loaded in which case clone it
		GeometryMap::iterator geomIt = m_Geometry.find(filename);
		if (geomIt != m_Geometry.end())
		{
			// texture found so return it
			return GeometryNodePtr(geomIt->second->Clone());
		}

		// load file and insert it in the map
		char fileCopy[255];
		strcpy(fileCopy, filename);
		char* token = strtok (fileCopy,".");
		while (token)
		{
			token = strtok (0, ".");
			// if it is a 3ds file use the the 3ds loader
			if (strcmp (token, "3ds") == 0)
			{			
				GeometryNodePtr geom = 
					GeometryNodePtr(Loader3ds::Load(filename, m_pRenderer));
				m_Geometry.insert(std::make_pair(filename, geom));
				return geom;				
			}
			// if it is a model file use the the model loader
			else if (strcmp (token, "model") == 0)
			{
				GeometryNodePtr geom = 
					GeometryNodePtr(LoaderModel::Load(filename, m_pRenderer));
				m_Geometry.insert(std::make_pair(filename, geom));
				return geom;	
			}

		}
	}
	
	return GeometryNodePtr();
}
//------------------------------------------------------
/**
* Loads a texture file
*/
TexturePropertyPtr CdnResourceLoader::
LoadTextureResource(const char* filename, bool bUseMipMaps)
{
	// check filename extension
	if (filename)
	{
		// see if the texture is already loaded
		TextureMap::iterator textureIt = m_Textures.find(filename);
		if (textureIt != m_Textures.end())
		{
			// texture found so return it
			return textureIt->second;
		}

		// break the filename into tokens so we can find the filename extension
		char fileCopy[255];
		strcpy(fileCopy, filename);
		char* token = strtok (fileCopy,".");
		// load file and insert it in the map
		while (token)
		{
			token = strtok (0, ".");
			// if it is a bmp file use the the bmp loader
			if (strcmp (token, "bmp") == 0)
			{
				// use OpenGL loader if OpenGL renderer
				if (m_pRenderer->GetRendererType() == CdnRenderer::TYPE_OPENGL)
				{
					TexturePropertyPtr texture = 
						TexturePropertyPtr(LoaderBmpGL::Load(filename, m_pRenderer, bUseMipMaps));
					if (texture)
					{
						m_Textures.insert(std::make_pair(filename, texture));
						return texture;
					}
					
				}
				// use D3d loader if D3d renderer (TODO)
				else
				{
					TexturePropertyPtr texture = 
						TexturePropertyPtr(LoaderBmpGL::Load(filename, m_pRenderer, bUseMipMaps));
					if (texture)
					{
						m_Textures.insert(std::make_pair(filename, texture));
						return texture;
					}			
				}
			}
			// if it is a tga file use the the tga loader
			else if (strcmp (token, "tga") == 0)
			{
				// use OpenGL loader if OpenGL renderer
				if (m_pRenderer->GetRendererType() == CdnRenderer::TYPE_OPENGL)
				{
					TexturePropertyPtr texture = 
						TexturePropertyPtr(LoaderTgaGL::Load(filename, m_pRenderer, bUseMipMaps));
					if (texture)
					{
						m_Textures.insert(std::make_pair(filename, texture));
						return texture;
					}
				}
				// use D3d loader if D3d renderer (TODO)
				else
				{
					TexturePropertyPtr texture = 
						TexturePropertyPtr(LoaderTgaGL::Load(filename, m_pRenderer, bUseMipMaps));
					if (texture)
					{
						m_Textures.insert(std::make_pair(filename, texture));
						return texture;
					}
				}
			}

		}
	}

	return TexturePropertyPtr();
}

//------------------------------------------------------
/**
* Returns a loaded texture
*/
TexturePropertyPtr CdnResourceLoader::GetTexture (const char* id)
{
	if (id)
	{
		TextureMap::iterator textureIt = m_Textures.find(id);
		if (textureIt != m_Textures.end())
		{
			return textureIt->second;
		}
	}
	

	return TexturePropertyPtr();
}

//------------------------------------------------------
/**
* Returns a loaded geometry node
*/
GeometryNodePtr CdnResourceLoader::GetGeometryNode (const char* id)
{
	if (id)
	{
		GeometryMap::iterator geomIt = m_Geometry.find(id);
		if (geomIt != m_Geometry.end())
		{
			// return clone
			return GeometryNodePtr(geomIt->second->Clone());
		}
	}
	

	return GeometryNodePtr();
}

//------------------------------------------------------
/**
* Creates numOfNodes GeometryNodes and puts them in preAllocatedResult
*/
void CdnResourceLoader::GetMultipleGeometryNodes(const char* id, 
											unsigned int numOfNodes,
											GeometryNodePtr preAllocatedResult[])
{
	GeometryNodePtr geometry = GetGeometryNode(id);
	if (geometry)
	{
		for (unsigned int i=0; i<numOfNodes; i++)
		{
			preAllocatedResult[i] = GeometryNodePtr(geometry->Clone());
		}
	}
	
}
//------------------------------------------------------
/**
* Unloads resources
*/
void CdnResourceLoader::UnloadResources()
{
	// unload textures
	TextureMap::iterator textureIt = m_Textures.begin();
	for(; textureIt != m_Textures.end(); ++textureIt)
	{
		// get api type and file type so that we know which loader
		// to use
		TexturePropertyPtr texture = textureIt->second;
		unsigned int apiType = texture->GetApiType();
		TextureProperty::TextureFileType fileType = texture->GetFileType();

		// if api is OpenGL
		if (apiType == CdnRenderer::TYPE_OPENGL)
		{
			// unload bmp
			if (fileType == TextureProperty::FILETYPE_BMP)
			{
				LoaderBmpGL::Unload(texture.get());
			}
			// unload tga
			else if (fileType == TextureProperty::FILETYPE_TGA)
			{
				LoaderTgaGL::Unload(texture.get());
			}
			// unload other
			else
			{

			}
		}
		else
		{
			// unload bmp
			if (fileType == TextureProperty::FILETYPE_BMP)
			{
				LoaderBmpGL::Unload(texture.get());
			}
			// unload tga
			else if (fileType == TextureProperty::FILETYPE_TGA)
			{
				LoaderTgaGL::Unload(texture.get());
			}
			// unload other
			else
			{

			}
		}
	}

	m_Textures.clear();

	// unload geometry
	m_pRenderer->UnloadGeometry();

	m_Geometry.clear();

}